﻿// <copyright file="TrackForm.cs" company="Microsoft">
// Copyright (c) 2010 Microsoft All Rights Reserved
// </copyright>
// <author>Microsoft</author>
// <email>mischero@microsoft.com</email>
// <date>2010-01-01</date>
// <summary>Web analytics service behavior class definition</summary>

namespace Microsoft.WebAnalytics.Behaviors
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.ComponentModel.Composition;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Interactivity;
    using Microsoft.WebAnalytics;
    using Microsoft.WebAnalytics.Contracts;
    using Microsoft.WebAnalytics.Data;

    /// <summary>
    /// <img src="../media/Microsoft.WebAnalytics.Behaviors.TrackForm.24x24.png"/> 
    /// Track Form Events
    /// </summary>
    /// <remarks>Add this behavior to Panel-derived classes like Grid and 
    /// StackPanel to track form controls that have TrackFormConrol behaviors 
    /// on them.</remarks>
    public class TrackForm : TriggerAction<Panel>
    {
        #region Fields
        /// <summary>
        /// IsTracking Custom Attached Property
        /// </summary>
        public static readonly DependencyProperty IsTrackingProperty = DependencyProperty.RegisterAttached(
          "IsTracking",
          typeof(bool),
          typeof(TrackForm),
          new PropertyMetadata(true));
        #endregion

        /// <summary>
        /// Initializes a new instance of the TrackForm class
        /// </summary>
        public TrackForm()
        {
            this.CustomProperties = new Collection<PropertyValue>();
        }

        #region Properties

        /// <summary>
        /// Gets or sets the category that will be applied to each form element and the Form Submit event.
        /// </summary>
        public string Category { get; set; }

        /// <summary>
        /// Gets or sets an integer that you can use to provide numerical data about the user event.
        /// </summary>
        /// <remarks>Default value is 0</remarks>
        [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "This should match AnalyticsEvent.Value."), 
        TypeConverter(typeof(NullableIntTypeConverter))]
        public int? Value { get; set; }

        /// <summary>
        /// Gets custom properties (name/value pairs) for the action
        /// </summary>
        public Collection<PropertyValue> CustomProperties { get; private set; }

        /// <summary>
        /// Gets or sets the event Logging method
        /// </summary>
        [Import("Log")]
        public Action<AnalyticsEvent> Log { get; set; }

        /// <summary>
        /// Gets or sets all of the Form Control Handlers
        /// </summary>
        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is the correct way to do Lazy MEF ImportMany."), 
        ImportMany(typeof(ITrackFormControl), AllowRecomposition = true)]
        public IEnumerable<Lazy<ITrackFormControl, ITrackerMetadata>> FormHandlers { get; set; }

        /// <summary>
        /// Gets or sets the custom actions that can be applied to the event
        /// </summary>
        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is the correct way to do MEF Lazy ImportMany."), ImportMany(typeof(ITrackHandler), AllowRecomposition = true)]
        public IEnumerable<Lazy<ITrackHandler, ITrackerMetadata>> Handlers { get; set; }

        #endregion

        #region Methods
        /// <summary>
        /// Sets the IsTracking Custom Attached property
        /// </summary>
        /// <param name="control">the control</param>
        /// <param name="value">whether tracking is enabled for this control</param>
        /// <exception cref="ArgumentNullException">if control is null</exception>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "We want to enforce putting this on Controls.")]
        public static void SetIsTracking(Control control, bool value)
        {
            if (control == null)
            {
                throw new ArgumentNullException("control");
            }

            control.SetValue(IsTrackingProperty, value);
        }

        /// <summary>
        /// Gets the IsTracking custom attached property
        /// </summary>
        /// <param name="control">the control</param>
        /// <returns>true if tracking is enabled for this control</returns>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "We want to enforce putting this on Controls.")]
        public static bool GetIsTracking(Control control)
        {
            if (control == null)
            {
                return false;
            }

            return (bool)control.GetValue(IsTrackingProperty);
        }

        /// <summary>
        /// Invoke the Submit event.
        /// </summary>
        /// <param name="parameter">the event argument parameter (typically coming from a Button Clicked event)</param>
        protected override void Invoke(object parameter)
        {
            var logEvent = new AnalyticsEvent()
            {
                Category = this.Category,
                EventArgs = this.CustomProperties,
                Name = "Submit",
                ObjectName = this.AssociatedObject.Name,
                ObjectType = this.AssociatedObject.GetType().Name,
                Value = this.Value
            };

            this.Log(logEvent);
        }

        /// <summary>
        /// Initialize parts and attach a Loaded handler
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();

#if WINDOWS_PHONE
            CompositionInitializer.SatisfyImports(this);
#else
            PartInitializer.SatisfyImports(this);
#endif

            this.AssociatedObject.Loaded += new RoutedEventHandler(this.OnLoaded);
        }

        #endregion

        #region Implementation

        /// <summary>
        /// Attach the events 
        /// </summary>
        /// <param name="sender">the panel element</param>
        /// <param name="e">the routed event arguments</param>
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            this.AttachEvents(this.AssociatedObject.Children);
        }

        /// <summary>
        /// Attach to events for each of the child controls of this Panel
        /// </summary>
        /// <param name="children">the UI Element collection</param>
        private void AttachEvents(UIElementCollection children)
        {
            foreach (var uiElement in children)
            {
                var panel = uiElement as Panel;

                if (panel != null)
                {
                    this.AttachEvents(panel.Children);

                    continue;
                }

                var control = uiElement as Control;

                if (!GetIsTracking(control))
                {
                    continue;
                }

                var trackers = from item in this.FormHandlers
                               where item.Metadata.ObjectType.IsInstanceOfType(control)
                               select item.Value;

                if (trackers.Count() == 0)
                {
                    System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Silverlight Analytics Framework: No FormTracker defined for {0} {1} which has a TrackFormControl behavior on it.", control.GetType().Name, control.Name));
                }
                else
                {
                    foreach (var tracker in trackers)
                    {
                        tracker.AttachEvents(control, new EventHandler<FormEventArgs>(this.HandleFormEvent));
                    }
                }
            }
        }

        /// <summary>
        /// Handle a form event
        /// </summary>
        /// <param name="sender">the <see cref="ITrackFormControl"/> derived class.</param>
        /// <param name="args">the form event arguments</param>
        private void HandleFormEvent(object sender, FormEventArgs args)
        {
            var logEvent = new AnalyticsEvent()
            {
                Category = this.Category,
                EventArgs = this.CustomProperties,
                Name = args.EventName,
                ObjectName = args.Control.Name,
                ObjectType = args.Control.GetType().Name,
                Value = this.Value
            };

            var trackers = from item in this.Handlers
                           where item.Metadata.ObjectType == args.Control.GetType()
                           select item.Value;

            if (trackers.Count() == 0)
            {
                System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Silverlight Analytics Framework: No Handler defined for {0} {1} which has a TrackFormControl behavior on it.", args.Control.GetType().Name, args.Control.Name));

                this.Log(logEvent);
            }
            else
            {
                foreach (var tracker in trackers)
                {
                    tracker.Track(this.AssociatedObject, logEvent, args.InnerEvent);
                }
            }
        }

        #endregion
    }
}
